package cn.t.facade.order;


import cn.t.constants.ParamConstants;
import cn.t.constants.ResponseError;
import cn.t.converter.order.ExceptionLogDTOConverter;
import cn.t.converter.order.OrderModelConverter;
import cn.t.core.exception.BizException;
import cn.t.core.facade.FacadeImpl;
import cn.t.core.mode.PageInfoDTO;
import cn.t.core.mode.PageQueryDTO;
import cn.t.dto.device.CargowayDTO;
import cn.t.dto.device.CargowaySkuDTO;
import cn.t.dto.device.vo.OpenCloseDTO;
import cn.t.dto.device.vo.QtyVO;
import cn.t.dto.order.DislodgeOrderDTO;
import cn.t.dto.order.ErrorDislodgeRequest;
import cn.t.dto.order.ExceptionLogDTO;
import cn.t.dto.order.OrderDetailDTO;
import cn.t.dto.order.OrderQueryRequest;
import cn.t.dto.order.ToNormalRequest;
import cn.t.dto.order.base.OrderSnRequest;
import cn.t.dto.order.base.VBaseDTO;
import cn.t.dto.order.excelresp.ErrorExcelResp;
import cn.t.enums.OptionTypeEnum;
import cn.t.enums.YesOrNoEnum;
import cn.t.enums.order.ErrorTypeEnum;
import cn.t.enums.order.MsvmOrderStatusEnum;
import cn.t.enums.order.OrderTransferStatusEnum;
import cn.t.enums.order.OrderTypeEnum;
import cn.t.facade.device.CargowayFacade;
import cn.t.facade.device.CargowaySkuFacade;
import cn.t.facade.user.UserInfoFacade;
import cn.t.facade.user.UserSessionFacade;
import cn.t.model.device.DeviceDO;
import cn.t.model.device.DeviceDataDO;
import cn.t.model.order.ExceptionLogDO;
import cn.t.model.order.OrderDO;
import cn.t.model.order.OrderDetailDO;
import cn.t.model.order.OverPickNumDO;
import cn.t.model.user.UserQueryDO;
import cn.t.service.device.DeviceDataService;
import cn.t.service.device.DeviceService;
import cn.t.service.order.ExceptionLogService;
import cn.t.service.order.OrderDetailService;
import cn.t.service.order.OrderService;
import cn.t.service.user.UserQueryService;
import cn.t.utils.DateUtil;
import cn.t.vo.order.DislodgeOrderDetailVO;
import cn.t.vo.order.ToNormalLineVO;
import cn.t.vo.user.FirmVO;
import cn.t.vo.user.ParticipantVO;
import cn.t.vo.user.UserSessionVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 设备异常日志记录表(ExceptionLog)服务编排
 *
 * @author t
 * @since 2024-05-24 10:19:34
 */
@Slf4j
@Service
@AllArgsConstructor
public class ExceptionLogFacade extends FacadeImpl<ExceptionLogDTO, ExceptionLogDO, ExceptionLogDTOConverter, ExceptionLogService> {
    public PageInfoDTO<DislodgeOrderDTO> getErrorList(PageQueryDTO<OrderQueryRequest> req) {
        List<OrderDO> list = orderService.getErrList(req);
        if (CollectionUtils.isEmpty(list)) {
            return new PageInfoDTO<>(req.getPageNo(), req.getPageSize(), 0, new ArrayList<>());
        }
        //数据处理
        List<DislodgeOrderDTO> listErr = commonOrderFacade.getList(list, OrderTypeEnum.ERRORDER_TYPE);
        return new PageInfoDTO<>(req.getPageNo(), req.getPageSize(), orderService.queryErrOrderCount(req.getParam()), listErr);
    }

    public List<ErrorExcelResp> openRecordExcel(ErrorDislodgeRequest param) {
        PageQueryDTO<OrderQueryRequest> requestPageQuery = new PageQueryDTO<>();
        requestPageQuery.setParam(param);
        requestPageQuery.setPageNo(ParamConstants.INT_0);
        requestPageQuery.setPageSize(ParamConstants.ORDER_PAGE_MAX_SIZE);
        PageInfoDTO<DislodgeOrderDTO> pageList = getErrorList(requestPageQuery);
        if (ObjectUtils.isEmpty(pageList)) {
            return Collections.emptyList();
        }
        //订单信息查询
        List<DislodgeOrderDTO> list = pageList.getRecords();

        if (ObjectUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        List<String> orderSnList = list.stream().map(DislodgeOrderDTO::getOrderSn).collect(Collectors.toList());
        //行异常数据分页处理
        List<ExceptionLogDO> errDetails = exportPageList(orderSnList);
        List<ErrorExcelResp> errorExcelRespList = list.stream().flatMap(orderDO -> errDetails.stream().map(errVo -> OrderModelConverter.convertErrBeanToExcel(errVo, orderDO))).collect(Collectors.toList());
        //异常类型为空
        if (param.getExceptionType() == null) {

            OrderDetailDO orderDetailDO = new OrderDetailDO();
            orderDetailDO.setOrderSnList(orderSnList);
            orderDetailDO.setOrderTransferStatus(OrderTransferStatusEnum.NOTRANSFER.getCode());
            List<OrderDetailDO> orderDetails = commonOrderFacade.exportPageList(orderDetailDO);
            List<ErrorExcelResp> details = list.stream().flatMap(orderDO -> orderDetails.stream().map(errVo -> OrderModelConverter.convertDetailToExcel(errVo, orderDO))).collect(Collectors.toList());
            if (ObjectUtils.isNotEmpty(details)) {
                errorExcelRespList.addAll(details);
            }
        }
        return errorExcelRespList;

    }

    public List<ExceptionLogDO> exportPageList(List<String> orderSnList) {
        int totalPages = OrderModelConverter.getTotalPages(orderSnList, ParamConstants.PAGE_MAX_SIZE);
        ;
        List<ExceptionLogDO> orderDetails = new ArrayList<>();
        for (int i = 0; i < totalPages; i++) {
            // 使用stream API切片列表
            List<String> orderNos = orderSnList.stream()
                    .skip((long) i * ParamConstants.PAGE_MAX_SIZE)
                    .limit(ParamConstants.PAGE_MAX_SIZE)
                    .collect(Collectors.toList());
            List<ExceptionLogDO> exceptionLogList = service.queryListByNo(orderNos);
            orderDetails.addAll(exceptionLogList);
            if (orderDetails.size() >= ParamConstants.EXPORTPAGEMAXSIZE) {
                break;
            }
        }
        return orderDetails.subList(0, Math.min(orderDetails.size(), ParamConstants.EXPORTPAGEMAXSIZE));
    }

    public VBaseDTO<DislodgeOrderDTO, DislodgeOrderDetailVO> getDislodgeErrInfo(OrderSnRequest param) {
        VBaseDTO<DislodgeOrderDTO, DislodgeOrderDetailVO> vBaseDTO = new VBaseDTO<>();
        String orderSn = param.getOrderSn();
        DislodgeOrderDTO dislodgeOrderDTO = commonOrderFacade.getDislodgeOrderDTO(orderSn);
        //行信息
        OrderDetailDTO orderDetailDTO = new OrderDetailDTO();
        orderDetailDTO.setOrderSn(orderSn);
        List<ExceptionLogDO> orderDetails = service.queryListByNo(Collections.singletonList(orderSn));
        List<DislodgeOrderDetailVO> collect = orderDetails.stream().map(OrderModelConverter::convertDislodgeErrVo).collect(Collectors.toList());
        vBaseDTO.setDetail(collect);
        vBaseDTO.setDataMain(dislodgeOrderDTO);
        return vBaseDTO;
    }


    @Transactional
    public void errorOrderToProcessed(OrderSnRequest req) {
        OrderDO orderDO = orderService.getOrderDO(req.getOrderSn());
        UserSessionVO session = userSessionFacade.getSession();
        //订单状态检查
        checkStatus(orderDO);
        OrderDO updateOrderDo = new OrderDO();
        updateOrderDo.setOrderTransferStatus(OrderTransferStatusEnum.PROCESSED.getCode());
        updateOrderDo.setTransferUserId(session.getUserId());
        updateOrderDo.setTransferUserName(session.getIndividualVO().getCustomerName());
        updateOrderDo.setGmtTransfer(new Date());
        updateOrderDo.setId(orderDO.getId());
        orderService.update(updateOrderDo);
    }

    public void checkStatus(OrderDO orderDO) {
        //订单已处理过，请勿重复操作
        if (!OrderTransferStatusEnum.NOTRANSFER.getCode().equals(orderDO.getOrderTransferStatus()) || !MsvmOrderStatusEnum.ERR_ORDER.getCode().equals(orderDO.getOrderStatus())) {
            throw new BizException(ResponseError.ORDER_STATUS_FAILED, String.format(ResponseError.ORDER_STATUS_FAILED.getName(), MsvmOrderStatusEnum.getDescByKey(orderDO.getOrderStatus()),
                    OrderTransferStatusEnum.getDescByKey(orderDO.getOrderTransferStatus())));
        }
    }

    @Transactional
    public void toErrorToNormal(ToNormalRequest req) {
        OrderDO orderDO = orderService.getOrderDO(req.getOrderSn());

        //订单状态检查
        checkStatus(orderDO);
        OrderDO orderN = new OrderDO();
        orderN.setId(orderDO.getId());
        orderN.setOrderStatus(MsvmOrderStatusEnum.CHANGETONORMAL.getCode());
        orderN.setOrderTransferStatus(OrderTransferStatusEnum.TRANSFERRED.getCode());
        orderN.setGmtTransfer(new Date());
        UserSessionVO session = userSessionFacade.getSession();
        if (session != null) {
            orderN.setTransferFirmId(session.firmInfo().getCustomerId());
            Optional<FirmVO> first = session.getFirms().stream().filter(firm -> firm.getCustomerId().equals(orderDO.getFirmId())).findFirst();
            first.ifPresent(firmVO -> orderN.setTransferParticipantId(firmVO.getParticipants().stream().map(ParticipantVO::getParticipantId).findFirst().orElse(null)));
            orderN.setTransferUserId(session.getUserId());
            orderN.setTransferUserName(session.getIndividualVO().getCustomerName());
        }
        if (CollectionUtils.isEmpty(req.getLines())) {
            String workOrderSn = processWorkOrder(orderDO);
            orderN.setWorkOrderSn(workOrderSn);
            orderService.update(orderN);
            return;
        }
        //获取异常列表
        Map<Long, ExceptionLogDO> exceptionLogMap = service.queryListByNo(Collections.singletonList(orderDO.getOrderSn())).stream().collect(Collectors.toMap(ExceptionLogDO::getId, Function.identity()));
        //获取货道商品表
        Map<Long, CargowaySkuDTO> cargowayGoodsList = cargowaySkuFacade.findByDeviceId(orderDO.getDeviceId()).stream().collect(Collectors.toMap(CargowaySkuDTO::getCargowayId, Function.identity()));

        //获取货道商品表
        Map<Long, CargowayDTO> cargowayMap = cargowayFacade.getInfoByDeviceId(orderDO.getDeviceId()).stream().collect(Collectors.toMap(CargowayDTO::getId, Function.identity()));
        List<ToNormalLineVO> lines = req.getLines();
        List<CargowaySkuDTO> cargowaySkuList = new ArrayList<>();
        List<OrderDetailDO> orderDetailList = new ArrayList<>();
        List<ExceptionLogDO> exceptionLogList = new ArrayList<>();
        lines.forEach(toNormalLineVO -> {
            ExceptionLogDO exceptionLogDO = exceptionLogMap.get(toNormalLineVO.getSid());
            if (ObjectUtils.isEmpty(exceptionLogDO)) {
                throw new BizException(ResponseError.ORDER_ERRCARWAY_NOT_FOUND, ResponseError.ORDER_ERRCARWAY_NOT_FOUND.getName());
            }
            if (ObjectUtils.isEmpty(exceptionLogDO.getCargowayNo())) {
                throw new BizException(ResponseError.ORDER_ERRCARWAY_NOT_FOUND, "货道号为空，不允许转成正常订单！");
            }
            CargowaySkuDTO cargowaySkuDO = cargowayGoodsList.get(exceptionLogDO.getCargowayId());
            CargowayDTO cargowayDTO = cargowayMap.get(exceptionLogDO.getCargowayId());

            if (ObjectUtils.isEmpty(cargowaySkuDO) || ObjectUtils.isEmpty(cargowayDTO)) {
                throw new BizException(ResponseError.ORDER_ERRCARWAY_ERR, ResponseError.ORDER_ERRCARWAY_ERR.getName());

            }
            if (!Objects.equals(cargowaySkuDO.getSkuCode(), exceptionLogDO.getSkuCode())) {
                throw new BizException(ResponseError.ORDER_UPDATE_FAILED, "货道商品已更换");
            }
            if (cargowaySkuDO.getInventory() < toNormalLineVO.getNum()) {
                throw new BizException(ResponseError.ORDER_UPDATE_FAILED, "系统库存不足");
            }
            //货道库存处理
            CargowaySkuDTO cargowaySkuN = new CargowaySkuDTO();
            cargowaySkuN.setId(cargowaySkuDO.getId());
            //减库存
            int inventory = cargowaySkuDO.getInventory() - toNormalLineVO.getNum();

            cargowaySkuN.setInventory(Math.max(inventory, 0));
            cargowaySkuList.add(cargowaySkuN);
            //修改异常表信息
            ExceptionLogDO exceptionLog = new ExceptionLogDO();
            exceptionLog.setId(exceptionLogDO.getId());
            exceptionLog.setOrderTransferStatus(OrderTransferStatusEnum.TRANSFERRED.getCode());
            exceptionLogList.add(exceptionLog);
            //往正常清单表插入数据
            OrderDetailDO orderGoodsBean = OrderModelConverter.convertToOrderGoods(exceptionLogDO, toNormalLineVO, cargowayDTO);
            orderDetailList.add(orderGoodsBean);
        });

        OverPickNumDO overPickNumDO = overPickupRecordFacade.checkOverPickupAndUpdate(orderDO, orderDetailList,Boolean.FALSE);
        Boolean aBoolean1 = orderDetailService.saveOrUpdateBatch(orderDetailList);
        Boolean aBoolean = service.updateBatch(exceptionLogList);
        Boolean aBoolean2 = cargowaySkuFacade.batchUpdate(cargowaySkuList);
        //系统创建的工单
        String workOrderSn = processWorkOrder(orderDO);
        orderN.setWorkOrderSn(workOrderSn);
        overPickupRecordFacade.addOverPick(overPickNumDO, orderDO);
        boolean update = orderService.update(orderN);
        if (!aBoolean && !aBoolean1 && !aBoolean2 && !update) {
            throw new BizException(ResponseError.ORDER_TO_NORMAL_FAILED, ResponseError.ORDER_TO_NORMAL_FAILED.getName());
        }


       // 超领
        boolean isOverPickup = !ObjectUtils.isEmpty(overPickNumDO.getOverPickupList());
        if (isOverPickup) {
            DeviceDO deviceDO = deviceService.findById(orderDO.getDeviceId());
            if (ObjectUtils.isEmpty(deviceDO)){
                throw new BizException(ResponseError.DEVICE_NOT_FOUND, ResponseError.DEVICE_NOT_FOUND.getName());
            }
            if ( YesOrNoEnum.YES.getCode().equals(deviceDO.getIsLock())){
                userInfoFacade.disableUser(orderDO.getUserId());
            }

        }


        //推送工单或邮件
        synchronizeWorkOrders.synchronizeWorkOrder(orderDO, orderDetailList, overPickNumDO.getOverPickupList());
    }

    private String processWorkOrder(OrderDO orderDO) {
        if (ObjectUtils.isEmpty(orderDO.getWorkOrderSn())) {
            UserQueryDO userQueryDO1 = new UserQueryDO();
            userQueryDO1.setId(orderDO.getUserId());
            UserQueryDO userQueryDO = userQueryService.findUserByFirmId(userQueryDO1).stream().findFirst().orElse(null);
            List<OrderDetailDO> allList = orderDetailService.queryListByOrderSn(new OrderDetailDO(orderDO.getOrderSn()));
            if (!ObjectUtils.isEmpty(allList)) {
                return workOrderFacade.addWorkOrder(orderDO, userQueryDO == null ? null : userQueryDO.getEmployeeCode(), allList);
            }
        }
        return orderDO.getWorkOrderSn();
    }

    @Transactional
    public void fixCloseDataAdd(List<ExceptionLogDO> list, OrderDO orderDO, DeviceDataDO deviceDataDO) {
        service.saveBatch(list);
        deviceDataService.add(deviceDataDO);
        orderService.add(orderDO);
    }


    public void exceptionAdd(OpenCloseDTO openCloseDO, ErrorTypeEnum errorTypeEnum) {
        ExceptionLogDO exceptionLogDO = converter.modelDo2Do(openCloseDO);
        exceptionLogDO.setBusinessType(openCloseDO.getOptionTypeEnum().getCode());
        if (errorTypeEnum != null) {
            exceptionLogDO.setExceptionType(errorTypeEnum.getCode());
            exceptionLogDO.setRemark(errorTypeEnum.getName());
        }
        openCloseDO.setErrorTypeEnum(errorTypeEnum);
        exceptionLogDO.setModifier(openCloseDO.getOperatorInfo());
        exceptionLogDO.setCreator(openCloseDO.getOperatorInfo());
        service.add(exceptionLogDO);
    }

    /**
     * 货道没有数据过滤
     */
    public void cargowayErrAdd(OpenCloseDTO openCloseWeightVO) {
        //判断上架货道不在qty里面的
        Map<Integer, Double> scaleMap = openCloseWeightVO.getScaleMap();
        Map<Integer, Double> openScaleMap = openCloseWeightVO.getOpenScale();
        List<QtyVO> errorList = openCloseWeightVO.getCargowaySkuMap().values().stream().map(cargowaySkuDTO -> {
            //开门两次数据货道未传
            Double openWeight = openScaleMap.get(cargowaySkuDTO.getCargowayNo());
            Double closeWeight = scaleMap.get(cargowaySkuDTO.getCargowayNo());
            if (closeWeight == null && openWeight == null) {
                QtyVO qtyN = new QtyVO();
                qtyN.setNo(cargowaySkuDTO.getCargowayNo());
                qtyN.setErrorTypeEnum(ErrorTypeEnum.ERROR4);
                return qtyN;
            } else {
                //未传开门货道数据
                if (closeWeight != null && openWeight == null) {
                    QtyVO qtyN = new QtyVO();
                    qtyN.setNo(cargowaySkuDTO.getCargowayNo());
                    qtyN.setErrorTypeEnum(ErrorTypeEnum.ERROR15);
                    return qtyN;
                }

            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
        List<ExceptionLogDO> err14List = errorList.stream().map
                (qtyErr -> converter.getExceptionLogDO(openCloseWeightVO, openCloseWeightVO.getCargowaySkuMap().get(qtyErr.getNo()),
                        qtyErr)).collect(Collectors.toList());
        if (!ObjectUtils.isEmpty(err14List)) {
            openCloseWeightVO.setErrorTypeEnum(ErrorTypeEnum.ERROR15);
            log.info("货道硬件损坏 或者 关门后未传开门重量数据：订单号{} {}", openCloseWeightVO.getOrderSn(), err14List.size());
            service.saveBatch(err14List);
        }
    }
    @Transactional
    public void addTimeOutError(OpenCloseDTO openCloseWeightVO) {
        //#补货关门超时判断，关门时间-开门时间>1800秒
        //#领用关门超时判断，关门时间-开门时间>180秒
        if (openCloseWeightVO.getRecordAt() != null && openCloseWeightVO.getOpenRecordAt() != null) {
            long time = (DateUtil.dateByStr(openCloseWeightVO.getRecordAt()).getTime() - openCloseWeightVO.getOpenRecordAt().getTime()) / 1000;
            if (openCloseWeightVO.getOptionTypeEnum().equals(OptionTypeEnum.rs_open) && time > ParamConstants.CABINET_OPEN_CLOSE_TIMEOUT) {
                openCloseWeightVO.setErrorTypeEnum(ErrorTypeEnum.ERROR17);
                exceptionAdd(openCloseWeightVO, ErrorTypeEnum.ERROR14);
            } else if (openCloseWeightVO.getOptionTypeEnum().equals(OptionTypeEnum.order_open) && time > ParamConstants.CABINET_OPEN_CLOSE_TIMEOUT_180) {
                openCloseWeightVO.setErrorTypeEnum(ErrorTypeEnum.ERROR17);
                exceptionAdd(openCloseWeightVO, ErrorTypeEnum.ERROR3);
            }
        }
    }


    public void addException(OpenCloseDTO openCloseWeightVO) {
        //货道数据结果处理不为空
        if (ObjectUtils.isNotEmpty(openCloseWeightVO.getQtyList())){
            List<QtyVO> errQtyList = openCloseWeightVO.getQtyList().stream().filter(qtyVO -> qtyVO.getErrorTypeEnum() != null).collect(Collectors.toList());
            List<ExceptionLogDO> errList = errQtyList.stream().map
                    (qtyErr -> converter.getExceptionLogDO(openCloseWeightVO, openCloseWeightVO.getCargowaySkuMap().get(qtyErr.getNo()),
                            qtyErr)).collect(Collectors.toList());
            if (!org.springframework.util.ObjectUtils.isEmpty(errList)) {
                log.info("异常记录条数量：订单号{} {}", openCloseWeightVO.getOrderSn(), errList.size());
                service.saveBatch(errList);
            }
        }else {
            //未补货
            if (OptionTypeEnum.rs_open.equals(openCloseWeightVO.getOptionTypeEnum())) {
                exceptionAdd(openCloseWeightVO, ErrorTypeEnum.ERROR24);
            }else if (OptionTypeEnum.order_open.equals(openCloseWeightVO.getOptionTypeEnum())){
                //未领取
                exceptionAdd(openCloseWeightVO, ErrorTypeEnum.ERROR10);
            }

        }

        //开关门两次数据长度不等
        if (openCloseWeightVO.getOpenScale().size() != openCloseWeightVO.getScale().size()) {

            exceptionAdd(openCloseWeightVO, ErrorTypeEnum.ERROR17);
        }
        //关门超时
        addTimeOutError(openCloseWeightVO);
        //货道没有数据
        cargowayErrAdd(openCloseWeightVO);
    }

    @Resource
    private OrderService orderService;
    @Resource
    private OverPickupRecordFacade overPickupRecordFacade;
    @Resource
    private OrderDetailService orderDetailService;

    @Resource
    private WorkOrderFacade workOrderFacade;
    @Resource
    private UserInfoFacade userInfoFacade;
    @Resource
    private SynchronizeWorkOrderFacade synchronizeWorkOrders;
    @Resource
    private UserSessionFacade userSessionFacade;
    @Resource
    private UserQueryService userQueryService;
    @Resource
    private CargowaySkuFacade cargowaySkuFacade;
    @Resource
    private CargowayFacade cargowayFacade;
    @Resource
    private CommonOrderFacade commonOrderFacade;
    @Resource
    private DeviceDataService deviceDataService;
    @Resource
    private DeviceService deviceService;
}

